/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package continuity;

import continuity.View.GameView;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import static java.awt.event.InputEvent.CTRL_DOWN_MASK;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

/**
 * A játék vezérlésért felelős osztály.
 * A prototípusban a parancsértelmezést is ő végzi.
 * @author FornetTeam
 */
public class Controller extends KeyAdapter {
    private static JFrame continuityFrame;
    
    private static GameView gameView;
    
    /**
     * A játék ütemezéséért lesz majd felelős.
     */
    private static volatile boolean isRunning = false;
    /**
     * A játék egy példánya.
     */
    private static Game game;
    /**
     * Az egyes karakterekhez előjegyzett mozgások irányát tárolja egy számként
     * (Direction.megfelelőirány.toInt() fgv. használható), ha nincs előjegyezve
     * mozgás akkor -1
     */
    private static int[] moveBuffer = {-1, -1};
    /**
     * Az egyes karakterekhez előjegyzett ugrást tárolja. Ha nincs előjegyezve
     * ugás, akkor false, ha van előjegyzett, ugrás akkor true.
     */
    private static boolean[] jumpBuffer = {false, false};
    /**
     * Az egyes szintekhez tartozó fájlok nevei tárolja.
     */
    public static List<String> levels;
    
    /**
     * A parancsértelmezést végzi.
     * @param args Az argumentumokat figyelmen kívül hagyjuk.
     * @throws IOException A függvény IOException-t dobhat.
     */
    public static void main(String args[]) {
        initLevels();
        initFrame();
        initKeyListener();
        initTimer();
        
        game = new Game();
    }
    /**
     * Az új játékpanel hozzáadása a Controllerhez.
     * @param gameView Az új játékpanel.
     */
    public static void setGameView(GameView gameView){
        isRunning=false;
        if (Controller.gameView != null){
            continuityFrame.remove(Controller.gameView);
        }
        Controller.gameView = gameView;
        gameView.refresh();
        if (Controller.gameView != null){
           continuityFrame.add(Controller.gameView, BorderLayout.CENTER);
        }
        continuityFrame.validate();
        continuityFrame.repaint();
    }
    
    private static void initLevels() {
        /**
         * Bemenetek betöltése.
         */
        BufferedReader file = null;
        try{
            /**
             * Ennek a fájlnak a sorait a levels-ben eltároljuk.
             */
            file = new BufferedReader(new FileReader("Levels.lst"));
            levels = new ArrayList<String>();
            String level = file.readLine();
            while (level != null){
                levels.add(level);
                level = file.readLine();
            }
            file.close();
        }  catch (IOException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private static void initFrame(){
        continuityFrame = new JFrame("Continuity");
        continuityFrame.setLayout(new BorderLayout());
        continuityFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        /**
         * A menü létrehozása.
         */
        JMenuBar menuBar = new JMenuBar();
        
        JMenu gameMenu = new JMenu("Játék");
        JMenuItem newGameMenuItem = new JMenuItem("Új játék");
        JMenuItem restartLevelMenuItem = new JMenuItem("Pálya újraindítása");
        JMenuItem nextLevelMenuItem = new JMenuItem("Következő pálya");
        JMenuItem exitMenuItem = new JMenuItem("Kilépés");
        
        JMenu helpMenu = new JMenu("Súgó");
        JMenuItem helpMenuItem = new JMenuItem("Súgó");
        JMenuItem aboutMenuItem = new JMenuItem("Névjegy");
        
        /**
         * Funkciók a Menükhöz
         */
        
        /**
         * Új játék
         */
        newGameMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (game != null){
                    isRunning = false;
                    game.initGame(0);                
                }
            }
        });
        newGameMenuItem.setAccelerator(KeyStroke.getKeyStroke('N', CTRL_DOWN_MASK));
        /**
         * A pálya újraindítása.
         */
        restartLevelMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (game != null){
                    isRunning = false;
                    game.restartLevel();
                }
            }
        });
        restartLevelMenuItem.setAccelerator(KeyStroke.getKeyStroke('R', CTRL_DOWN_MASK));
        
        /**
         * A következő pálya elindítása.
         */
        nextLevelMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (game != null){
                    isRunning = false;
                    game.nextLevel();
                }
            }
        });
        nextLevelMenuItem.setAccelerator(KeyStroke.getKeyStroke('X', CTRL_DOWN_MASK));
        /**
         * Kilépés a játékból.
         */
        exitMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        
       /**
        * A súgó megjelenítése.
        */ 
        helpMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(null,
                            "A space billentyű lenyomásával lehet váltani a kétfajta nézet között. \n"
                        + "-Az alapértelmezett nézetnél a szobákat tudod mozgatni a nyílbillentyűk segítségével. \n"
                        + "-Az osztott nézetben mindig az aktuális karakter van középen, és őt irányíthatod. A bal oldali karakter az A,W és D billentyűkkel,\n a jobb oldali pedig a nyílbillentyűkkel mozgatható.\n\n"
                        + "Ha lezuhansz egy szakadékba és nincs vagy az aktuális szobával nem kompatibilis az aktuális szobától lefelé lévő szoba,\n akkor a karaktered meghal és az előzőleg felvett kulcs helyén, illetve annak hiányában a kezdeti pozícióban újjáéled.\n"+
                        "A játéknak akkor van vége, ha felvették a karakterek az összes kulcsot és rálépnek mindketten az ajtóra.");
            }
        });
       
       /**
        * A névjegy megjelenítése.
        */
        aboutMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(null,
                            "BME-VIK Szoftverlaboratórium 4 házi feladat \n"
                        + "Készítette: FornetTeam \n"
                        + "Tagok: Barish Barna, Berecz Dénes, Berényi Kristóf, Gönczi Tamás\n"
                        + "e-mail: fornetteam{csiga}googlegroups{pötty}com");
            }
        });

        /**
            * Az elemek hozzáadása az ablakhoz.
            */
        gameMenu.add(newGameMenuItem);
        gameMenu.add(restartLevelMenuItem);
        gameMenu.add(nextLevelMenuItem);
        gameMenu.add(exitMenuItem);
        helpMenu.add(helpMenuItem);
        helpMenu.add(aboutMenuItem);
        menuBar.add(gameMenu);
        menuBar.add(helpMenu);
        continuityFrame.add(menuBar, BorderLayout.NORTH);

        continuityFrame.setSize(1000,600);
        continuityFrame.setVisible(true);
    }

    /**
     * A KeyListener inicalizálása.
     */
    private static void initKeyListener() {
       continuityFrame.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (gameView != null){
                    if (gameView.getZoom()){
                       /**
                        * Ha rá vagyunk zoomolva a karakterekre, akkor mozgathatjuk
                        * őket.
                        */
                        switch (e.getKeyCode()){
                            case Parameters.Character0LeftKeyCode :
                                moveBuffer[0] = 0;
                                break;
                            case Parameters.Character0UpKeyCode :
                                jumpBuffer[0] = true;
                                break;
                            case Parameters.Character0RightKeyCode :
                                moveBuffer[0] = 2;
                                break;
                            case Parameters.Character1LeftKeyCode :
                                moveBuffer[1] = 0;
                                break;
                            case Parameters.Character1UpKeyCode :
                                jumpBuffer[1] = true;
                                break;
                            case Parameters.Character1RightKeyCode :
                                moveBuffer[1] = 2;
                                break;
                            case Parameters.zoomKeyCode:
                                gameView.setZoom(false);
                                gameView.refresh();
                                isRunning = false;
                                break;
                        }
                    } else {
                        /**
                        * Ha nem vagyunk rázoomolva a karakterekre, akkor
                        * a pályaelemeket mozgathatjuk..
                        */
                        switch (e.getKeyCode()){
                            case Parameters.SwapLeftKeyCode :
                                game.swap(Direction.Right);
                                break;
                            case Parameters.SwapDownKeyCode :
                                game.swap(Direction.Up);
                                break;
                            case Parameters.SwapUpKeyCode :
                                game.swap(Direction.Down);
                                break;
                            case Parameters.SwapRightKeyCode :
                                game.swap(Direction.Left);
                                break;
                            case Parameters.zoomKeyCode:
                                gameView.setZoom(true);
                                gameView.refresh();
                                isRunning = true;
                                break;
                        }
                    }
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {
                switch (e.getKeyCode()){
                    case Parameters.Character0LeftKeyCode :
                        moveBuffer[0] = -1;
                        break;
                    case Parameters.Character0UpKeyCode :
                        jumpBuffer[0] = false;
                        break;
                    case Parameters.Character0RightKeyCode :
                        moveBuffer[0] = -1;
                        break;
                    case Parameters.Character1LeftKeyCode :
                        moveBuffer[1] = -1;
                        break;
                    case Parameters.Character1UpKeyCode :
                        jumpBuffer[1] = false;
                        break;
                    case Parameters.Character1RightKeyCode :
                        moveBuffer[1] = -1;
                        break;
                }
            }
            
       });
    }

    /**
     * A Timer inicializálása.
     */
    private static void initTimer() {
        Thread CharacterThread = new Thread(){
            @Override
            public void run() {
                while (true){
                    while (!isRunning){
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                    Direction[] dirs;
                    boolean happenedSomething = false;
                    /**
                     * Ugrási bufferek.
                     */
                    if (jumpBuffer[0] == true){
                        game.jump(0);
                    }
                    if (jumpBuffer[1] == true){
                        game.jump(1);
                    }

                    /**
                     * Fall függvény hívása.
                     */
                    dirs = new Direction[2];
                    dirs[0] = game.fall(0);
                    dirs[1] = game.fall(1);
                    if (dirs[0] != null || dirs[1] != null){
                        happenedSomething = true;
                        gameView.fall(dirs);
                    }
                    
                    /**
                     * Mozgási bufferek
                     */
                    dirs = new Direction[2];
                    if (moveBuffer[0] != -1){
                        Direction dir = Direction.intToDirection(moveBuffer[0]);
                        if (game.move(0,dir)){
                            dirs[0] = dir;
                        }
                    }
                    if (moveBuffer[1] != -1){
                        Direction dir = Direction.intToDirection(moveBuffer[1]);
                        if (game.move(1,dir)){
                            dirs[1] = dir;
                        }
                    }
                    if (dirs[0] != null || dirs[1] != null){
                        happenedSomething = true;
                        gameView.move(dirs);
                    }
                    
                    /**
                     * Ha nem történ mozgás, akkor alszunk.
                     */
                    if (!happenedSomething){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            }
        };
        CharacterThread.start();
    }
}
